package jblip.util;

import java.util.Date;
import java.util.List;
import java.util.Map;

import jblip.resources.Media;
import jblip.resources.ResourcesFactory;
import jblip.resources.Shortlink;
import jblip.resources.Subscription;
import jblip.resources.Transport;
import jblip.resources.Update;
import jblip.resources.UpdateType;
import jblip.resources.User;
import jblip.resources.UserPicture;
import jblip.resources.Media.MediaType;
import jblip.resources.UserPicture.PictureSize;

/**
 * Utility class for creating resources from provided String to Object maps.
 * 
 * <p>
 * The maps passed to the factory should follow a common entry scheme (but note
 * the expecptions listed below!):
 * <ul>
 * <li>Keys should reflect getter name, with 'get' prefix stripped out,
 * subsequent words in a method written lowercase and joined with a single '_'
 * sign. The acronyms are treated as a single word.
 * <li>Values should be the object returned by the corresponding method.
 * </ul>
 * This way the 'getUpdate' method looks for mapping from "update" to an Update
 * object, while 'getUpdatePath' method looks for mapping from "update_path" to
 * a String. </p>
 * <p>The results returned by various methods might be inconsistent
 * &mdash; the user specifies all the fields so (e.g.) the value returned by
 * getSrcUser() might not have its login equal to string obtained through
 * getSrcUserLogin().
 * </p>
 * <p>
 * There are a few exceptions to this rule:
 * <ul>
 * <li>{@link #getMedia(Object, MediaType)} returns the Media object which in
 * turn returns (through {@link Media#getType()} whatever the MediaType argument
 * was passed to the factory method.
 * <li>{@link #getTransport(Object)} simply returns the input object casted to
 * Transport interface.
 * <li>{@link #getUserPicture(Object)} returns the UserPicture object in which
 * there is a parameterized getter {@link UserPicture#getURL(PictureSize)}.
 * the keys used for this calls have the form 
 * <code>url_ + size.toString().toLowerCase()</code>. 
 * </ul>
 * </p>
 * 
 * @author Krzysztof Sroka
 */
public class MapResourceFactory implements ResourcesFactory {

  @Override
  public Media getMedia(Object object, final MediaType type) {
    if (object instanceof Map) {
      final Map<?, ?> map = (Map<?, ?>) object;
      return new Media() {
        private static final long serialVersionUID = 1L;

        @Override
        public String getUpdatePath() {
          return (String) map.get("update_path");
        }

        @Override
        public Update getUpdate() {
          return (Update) map.get("update");
        }

        @Override
        public String getURL() {
          return (String) map.get("url");
        }

        @Override
        public MediaType getType() {
          return type;
        }

        @Override
        public Integer getID() {
          return (Integer) map.get("id");
        }
      };
    }
    return null;
  }

  @Override
  public Shortlink getShortlink(Object object) {
    if (object instanceof Map) {
      final Map<?, ?> map = (Map<?, ?>) object;
      return new Shortlink() {
        private static final long serialVersionUID = 1L;

        @Override
        public String getShortcode() {
          return (String) map.get("shortcode");
        }

        @Override
        public String getOriginalURL() {
          return (String) map.get("original_url");
        }

        @Override
        public Integer getID() {
          return (Integer) map.get("id");
        }

        @Override
        public Integer getHitCount() {
          return (Integer) map.get("hit_count");
        }

        @Override
        public Date getCreatedAt() {
          return (Date) map.get("created_at");
        }
      };
    }

    return null;
  }

  @Override
  public Subscription getSubscription(Object object) {
    if (object instanceof Map) {
      final Map<?, ?> map = (Map<?, ?>) object;
      return new Subscription() {
        private static final long serialVersionUID = 1L;

        @Override
        public Transport getTransport() {
          return (Transport) map.get("transport");
        }

        @Override
        public String getTrackingUserName() {
          return (String) map.get("tracking_user_name");
        }

        @Override
        public User getTrackingUser() {
          return (User) map.get("tracking_user");
        }

        @Override
        public String getTrackedUserName() {
          return (String) map.get("tracked_user_name");
        }

        @Override
        public User getTrackedUser() {
          return (User) map.get("tracked_user");
        }
      };
    }

    return null;
  }

  @Override
  public Transport getTransport(Object object) {
    return (Transport) object;
  }

  @Override
  public Update getUpdate(Object object) {
    if (object instanceof Map) {
      final Map<?, ?> map = (Map<?, ?>) object;
      return new Update() {
        private static final long serialVersionUID = 1L;

        @Override
        public UpdateType getType() {
          return (UpdateType) map.get("type");
        }

        @Override
        public Transport getTransport() {
          return (Transport) map.get("transport");
        }

        @Override
        public String getSrcUserLogin() {
          return (String) map.get("src_user_login");
        }

        @Override
        public User getSrcUser() {
          return (User) map.get("src_user");
        }

        @Override
        public Integer getId() {
          return (Integer) map.get("id");
        }

        @Override
        public String getDstUserLogin() {
          return (String) map.get("dst_user_login");
        }

        @Override
        public User getDstUser() {
          return (User) map.get("dst_user");
        }

        @Override
        public Long getCreatedAtEpoch() {
          return (Long) map.get("created_at_epoch");
        }

        @Override
        public Date getCreatedAt() {
          return (Date) map.get("created_at");
        }

        @Override
        public String getBody() {
          return (String) map.get("body");
        }

        @Override
        public String getAttachedRecordingPath() {
          return (String) map.get("attached_recording_path");
        }

        @Override
        public Media getAttachedRecording() {
          return (Media) map.get("attached_recording");
        }

        @Override
        public String getAttachedPicturesPath() {
          return (String) map.get("attached_pictures_path");
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<? extends Media> getAttachedPictures() {
          return (List<? extends Media>) map.get("attached_pictures");
        }

        @Override
        public String getAttachedMoviePath() {
          return (String) map.get("attached_movie_path");
        }

        @Override
        public Media getAttachedMovie() {
          return (Media) map.get("attached_movie");
        }
      };
    }

    return null;
  }

  @Override
  public User getUser(Object object) {
    if (object instanceof Map) {
      final Map<?, ?> map = (Map<?, ?>) object;
      return new User() {
        private static final long serialVersionUID = 1L;

        @Override
        public Sex getSex() {
          return (Sex) map.get("sex");
        }

        @Override
        public String getLogin() {
          return (String) map.get("login");

        }

        @Override
        public String getLocation() {
          return (String) map.get("location");
        }

        @Override
        public Integer getID() {
          return (Integer) map.get("id");
        }

        @Override
        public Update getCurrentStatus() {
          return (Update) map.get("current_status");
        }

        @Override
        public String getBackgroundPath() {
          return (String) map.get("background_path");
        }

        @Override
        public Media getBackground() {
          return (Media) map.get("background");
        }

        @Override
        public String getAvatarPath() {
          return (String) map.get("avatar_path");
        }

        @Override
        public UserPicture getAvatar() {
          return (UserPicture) map.get("avatar");
        }
      };
    }

    return null;
  }

  @Override
  public UserPicture getUserPicture(Object object) {
    if (object instanceof Map) {
      final Map<?, ?> map = (Map<?, ?>) object;
      return new UserPicture() {
        private static final long serialVersionUID = 1L;

        @Override
        public String getURL(PictureSize size) {
          final String key = "url_" + size.toString().toLowerCase();
          return (String) map.get(key);
        }

        @Override
        public String getURL() {
          return (String) map.get("url");
        }

        @Override
        public Integer getID() {
          return (Integer) map.get("id");
        }
      };
    }

    return null;
  }

}
